a tool for shared writing and social publishing
at feature/reader 151 lines 5.3 kB view raw
1import { Metadata } from "next"; 2import * as Y from "yjs"; 3import * as base64 from "base64-js"; 4 5import type { Fact } from "src/replicache"; 6import type { Attribute } from "src/replicache/attributes"; 7import { YJSFragmentToString } from "components/Blocks/TextBlock/RenderYJSFragment"; 8import { Leaflet } from "./Leaflet"; 9import { scanIndexLocal } from "src/replicache/utils"; 10import { getRSVPData } from "actions/getRSVPData"; 11import { PageSWRDataProvider } from "components/PageSWRDataProvider"; 12import { getPollData } from "actions/pollActions"; 13import { supabaseServerClient } from "supabase/serverClient"; 14import { get_leaflet_data } from "app/api/rpc/[command]/get_leaflet_data"; 15import { NotFoundLayout } from "components/PageLayouts/NotFoundLayout"; 16 17export const preferredRegion = ["sfo1"]; 18export const dynamic = "force-dynamic"; 19export const fetchCache = "force-no-store"; 20 21type Props = { 22 // this is now a token id not leaflet! Should probs rename 23 params: Promise<{ leaflet_id: string }>; 24}; 25export default async function LeafletPage(props: Props) { 26 let { result: res } = await get_leaflet_data.handler( 27 { token_id: (await props.params).leaflet_id }, 28 { supabase: supabaseServerClient }, 29 ); 30 let rootEntity = res.data?.root_entity; 31 if (!rootEntity || !res.data || res.data.blocked_by_admin) 32 return ( 33 <NotFoundLayout> 34 <p className="font-bold">Sorry, we can't find this leaflet!</p> 35 <p> 36 This may be a glitch on our end. If the issue persists please{" "} 37 <a href="mailto:contact@leaflet.pub">send us a note</a>. 38 </p> 39 </NotFoundLayout> 40 ); 41 42 let [{ data }, rsvp_data, poll_data] = await Promise.all([ 43 supabaseServerClient.rpc("get_facts", { 44 root: rootEntity, 45 }), 46 getRSVPData(res.data.permission_token_rights.map((ptr) => ptr.entity_set)), 47 getPollData(res.data.permission_token_rights.map((ptr) => ptr.entity_set)), 48 ]); 49 let initialFacts = (data as unknown as Fact<Attribute>[]) || []; 50 return ( 51 <PageSWRDataProvider 52 rsvp_data={rsvp_data} 53 poll_data={poll_data} 54 leaflet_id={res.data.id} 55 leaflet_data={res} 56 > 57 <Leaflet 58 initialFacts={initialFacts} 59 leaflet_id={rootEntity} 60 token={res.data} 61 /> 62 </PageSWRDataProvider> 63 ); 64} 65 66export async function generateMetadata(props: Props): Promise<Metadata> { 67 let { result: res } = await get_leaflet_data.handler( 68 { token_id: (await props.params).leaflet_id }, 69 { supabase: supabaseServerClient }, 70 ); 71 let rootEntity = res.data?.root_entity; 72 if (!rootEntity || !res.data) return { title: "Leaflet not found" }; 73 let publication_data = 74 res.data?.leaflets_in_publications?.[0] || 75 res.data?.permission_token_rights[0].entity_sets?.permission_tokens?.find( 76 (p) => p.leaflets_in_publications.length, 77 )?.leaflets_in_publications?.[0]; 78 if (publication_data) { 79 return { 80 title: publication_data.title || "Untitled", 81 description: publication_data.description, 82 }; 83 } 84 let { data } = await supabaseServerClient.rpc("get_facts", { 85 root: rootEntity, 86 }); 87 let initialFacts = (data as unknown as Fact<Attribute>[]) || []; 88 let scan = scanIndexLocal(initialFacts); 89 let firstPage = 90 scan.eav(rootEntity, "root/page")[0]?.data.value || rootEntity; 91 let pageType = scan.eav(firstPage, "page/type")[0]?.data.value || "doc"; 92 let firstBlock, secondBlock; 93 if (pageType === "canvas") { 94 [firstBlock, secondBlock] = scan 95 .eav(firstPage, "canvas/block") 96 .map((b) => { 97 let type = scan.eav(b.data.value, "block/type"); 98 if (!type[0]) return null; 99 return { 100 ...b.data, 101 type: type[0].data.value, 102 }; 103 }) 104 .filter((b) => b !== null) 105 .filter((b) => b.type === "text" || b.type === "heading") 106 .sort((a, b) => { 107 if (a.position.y === b.position.y) { 108 return a.position.x - b.position.x; 109 } 110 return a.position.y - b.position.y; 111 }); 112 } else { 113 [firstBlock, secondBlock] = scan 114 .eav(firstPage, "card/block") 115 .map((b) => { 116 let type = scan.eav(b.data.value, "block/type"); 117 return { 118 ...b.data, 119 type: type[0]?.data.value, 120 }; 121 }) 122 123 .filter((b) => b.type === "text" || b.type === "heading") 124 .sort((a, b) => (a.position > b.position ? 1 : -1)); 125 } 126 let metadata: Metadata = { title: "Untitled Leaflet", description: " " }; 127 128 let titleFact = initialFacts.find( 129 (f) => f.entity === firstBlock?.value && f.attribute === "block/text", 130 ) as Fact<"block/text"> | undefined; 131 if (titleFact) { 132 let doc = new Y.Doc(); 133 const update = base64.toByteArray(titleFact.data.value); 134 Y.applyUpdate(doc, update); 135 let nodes = doc.getXmlElement("prosemirror").toArray(); 136 metadata.title = YJSFragmentToString(nodes[0]); 137 } 138 139 let descriptionFact = initialFacts.find( 140 (f) => f.entity === secondBlock?.value && f.attribute === "block/text", 141 ) as Fact<"block/text"> | undefined; 142 if (descriptionFact) { 143 let doc = new Y.Doc(); 144 const update = base64.toByteArray(descriptionFact.data.value); 145 Y.applyUpdate(doc, update); 146 let nodes = doc.getXmlElement("prosemirror").toArray(); 147 metadata.description = YJSFragmentToString(nodes[0]); 148 } 149 150 return metadata; 151}